<?php
/**
 * filter.class
 *
 * script
 *
 * @version:	0.1a
 * @package:	global
 * @author:	alex_ez
 * @created:	21.05.2009 05:13:12
 *
 * @copyright:	(c) 2009 alex <qfox@ya.ru> yaroshevich aka alex_ez. All rights reserved.
 */

class FilterException extends Exception { }

class Filter
{
	private $owner = null;
	private $url = null;
	
	private $options = array( );
	private $lists = array( );
	
	/**
	 * limited
	 * default value, list, and key
	 */
	private $limit = 1; // default value
	private $limits = array( 0, 20, 50, 200, 1000 );
	private $limitkey = '__limit';
	
	
	public function __construct( Controller $c, $fieldsurl = null, array $options = null )
	{
		$this ->owner = $c;
		$this ->url = (!is_null($fieldsurl)) ? ($fieldsurl) : $c ->getInnerUrl( );
		
		$this ->limits = array_filter( $this ->limits );
		$this ->options['mode'] = array( );
		
		$this ->setOptions( array(
			'action' => $this ->url,
			'method' => 'get',
			'name'	=> str_replace( '/', '_', trim( $this->url, '/' ) ),
			) );
		
		$this ->setOptions( $options );
	}
	
	public function setOption( $k, $v )
	{
		$k = mb_strtolower( $k );
		if( $k == 'mode' )
		{
			if( !is_string( $v ) ) return;
			$modes = array_map( 'trim', array_map( 'strtolower', explode(',',$v) ) );
			
			foreach( array_filter( $modes ) as $vk )
				$this ->options[ $k ][ ltrim( $vk, ' -!' ) ] = (strpos( '-!', $vk[0] ) === false);
		}
		elseif( $k == 'method' )
			$this ->options[ $k ] = strtolower( $v );
		elseif( $k == 'limits' )
		{
			$this ->limits = array_filter( array_map( 'intval', explode( ',', ','.$v ) ) );
		}
		else
			$this ->options[ $k ] = $v;
	}
	
	public function setOptions( $a )
	{
		if( !is_array( $a ) ) return;
		
		foreach( $a as $k => $v )
			$this ->setOption( $k, $v );
	}
	
	public function getOptions( )
	{
		return json::encode( $this ->options );
	}
	
	public function getFormattedData( )
	{
		$formatted = array( );
		$data = $this ->getPostData( );
		$fields = $this ->getPostFields( );
		
		foreach( $fields as $k => $f )
		{
			if( $f['type'] == 'list' || $f['type'] == 'radio' )
			{
				$values = _a::flat( $f['values'], 'id', 'title' );
				$value = $values[$data[$k]];
			}
			else
				$value = $data[$k];
			
			$formatted[ $f['label'] ] = $value;
		}
		
		return $formatted;
	}
	
	public function setData( $data )
	{
		$this ->data = is_array( $data )
			? ( isset($data['data']) && is_string( $data['data'] ) ? json::decode( $data ) : $data )
			: json::decode( $data );
	}
	
	
	public function setLists( $a, $template = null )
	{
		foreach( $a as $k => $v )
		{
			$this ->setList( $k, $v, $template );
		}
	}
	
	public function setList( $var, $list, $template = null, $deeper = false )
	{
		if( !$template ) $template = "{title}";
		
		$this ->lists[ $var ] = $this ->setList_operate( $list, $template, $deeper );
	}

	// handler
	private function setList_operate( $list, $template, $deeper = false )
	{
		$r = array( );
		
		foreach( $list as $k => $i )
		{
			// gg
			if( $deeper && is_array( $i ) && !isset( $i['id'] ) )
			{
				$r[$k] = $this ->setList_operate( $i, $template );
				continue;
			}
			
			// classic
			$id = is_array( $i ) && isset( $i['id'] ) ? $i['id'] : $k;
			
			$title = !is_array( $i )
				? $i
				: ( str_replace(
				array_map( create_function('$v','return "{{$v}}";'), array_keys( $i ) ),
				array_map( 'strval', array_values( $i ) ),
				(string)$template ) );
			
			$r[$k] = array( 'id' => $id, 'title' => $title );
		}
		
		return $r;
	}
	
	public function getList( $var )
	{
		if( !isset( $this->lists[$var] ) )
			$this ->setList( $var, explode(',',$var) );
		
		return $this->lists[$var];
	}
	
	public function getFieldSet( $url = null, $decode = false )
	{
		$url = !is_null( $url ) ? $url : $this ->url;
		
		foreach( array( $url.'#filter', $url ) as $i )
		{
			$r = (array)($this ->owner ->getFieldSet( $i, 1 ));
			if( !empty( $r ) ) break;
		}
		
		if( empty( $r ) )
			fb::log( $url, 'filter: no fields' );
		
		if( @$this ->options['mode']['limited'] )
		{
			$r[$this->limitkey] = array( 'type' => 'list', 'label' => 'Q-ty', 'values' => $this ->limitkey.'s' );
		}
		
		// set values
		foreach( array_keys( $r ) as $k )
		{
			// static fields are for filter
			if( $r[$k]['type'] == 'static' )
				$r[$k]['type'] = 'string';
			
			// fill lists
			if( ( $r[$k]['type'] == 'list' || $r[$k]['type'] == 'radio' ) && isset( $r[$k]['values'] ) )
			{
				$r[$k]['values'] = $this ->getList( $r[$k]['values'] );
				//fb::log( $r[$k]['values'], 'values' );
			}
			
			// set defaults
			if( isset( $this ->data[$k] ) && !(isset($r[$k]['const'])&&$r[$k]['const']) ) {
				$r[$k]['value'] = $this ->data[ $k ];
			} else {
				//fb::log( 'not changed', $k );
			}
			if( $r[$k]['type'] == 'money' && isset($r[$k]['value']) )
				$r[$k]['value'] = number_format( $r[$k]['value'], 2, '.', ',' );
		}
		
		return $decode ? $r : json::encode( $r );
	}
	
	private function getPostFields( )
	{
		$r = array( );
		$fs = $this ->getFieldSet( $this ->url, 1 );
		foreach( $fs as $k => $f )
		if( !strpos( ',header,static', $f['type'] ) )
		if( $k != $this ->limitkey )
		{
			$r[$k] = $f;
		}
		
		return $r;
	}
	
	public function getPostData( $needKey = null )
	{
		$fs = is_null( $needKey ) ? array_keys( $this ->getPostFields( ) ) : (array)( $needKey );
		
		$r = request::__getMe( );
		$_r = array( );
		
		$key = $this ->options['method'].'_'.($this ->options['name']);
		$posted = $r ->$key;
		
		foreach( $fs as $k )
		{
			$v = isset( $posted[ $k ] ) ? $posted[ $k ] : ( isset( $this ->data[ $k ] ) ? $this ->data[ $k ] : null );
			$_r[$k] = $v;
		}
		
		if( is_scalar( $needKey ) )
		return isset( $_r[$needKey] ) ? $_r[$needKey] : null;
		
		return $_r;
	}
	
	public function getWhereData( )
	{
		$data = array( );
		$fs = $this ->getPostFields( );
		
		$q = @$this->options['mode']['strictwhere']?'':'~'; // qualifier
		
		foreach( $this ->getPostData( ) as $k => $v )
		if( !is_null( $v ) && $v !== "" )
		{
			$operator = @$fs[$k]['operator'];
			
			$type = $fs[$k]['type'];
			switch( $type )
			{
			case 'list':
			case 'radio':
				if( !isset( $fs[$k]['values'][$v] ) ) continue 2;
				break;
			case 'date':
			case 'time':
			case 'datetime':
			case 'month':
				$dtf =	array( 'datetime' => 'Y-m-d H:i:s', 'date' => 'Y-m-d', 'time' => 'H:i:s', 'month' => 'Y-m-01' );
				$min =	array( 'datetime' => '0000-00-00 00:00:00', 'date' => '0000-00-00', 'time' => '00:00:00', 'month' => '0000-00-00' );
				$max =	array( 'datetime' => '9999-12-31 23:59:59', 'date' => '9999-12-31', 'time' => '23:59:59', 'month' => '9999-12-31' );
				// @ or from+to
				if( !@$fs[$k]['period'] ) {
				$v = @date_format( date_create( $v ), $dtf[$type] ); if( $v===false ) continue 2;
				} else {
				$vf = mb_strlen($v['from']) ? @date_format( date_create( $v['from'] ), $dtf[$type] ) : false;
				$vt = mb_strlen($v['to']) ? @date_format( date_create( $v['to'] ), $dtf[$type] ) : false;
				if( $vf===false && $vt === false ) continue 2;
				$v = array( $vf===false ? $min[$type] : $vf, $vt===false ? $max[$type] : $vt ); $operator = '^';
				}
				break;
			}
			
			$k .= _::pick( $operator, ($type!='number'&&$type!='list'&&$type!='radio') ? $q : '' );
			$data[$k] = $v;
		}
		
		return $data;
	}
	
	public function getQuery( $addon = null )
	{
		$fid = $this->options['name'];
		$data = array( );
		
		$data[$fid][$this->limitkey] = $this ->getLimit( );
		
		$fs = $this ->getPostFields( );
		
		foreach( $this ->getPostData( ) as $k => $v )
		if( !is_null( $v ) && $v !== "" )
			$data[$fid][$k] = $v;
		
		$data += (array)($addon);
		
		return http_build_query( $data, 'flags' );
	}

	public function getLimit( )
	{
		$r = $this ->getPostData( $this ->limitkey );
		
		if( !isset( $this ->limits[ $r ] ) ) $r = $this ->limit;
		if( !isset( $this ->limits[ $r ] ) ) $r = 1;
		
		return $r;
	}
	
	public function getLimitValue( )
	{
		return $this ->limits[ $this ->getLimit( ) ];
		
		return (int)(( isset( $this ->data[ $this ->limitkey ] ) && isset( $this ->limits[ $this ->data[ $this ->limitkey ] ] ) )
			? $this ->limits[ $this ->data[ $this ->limitkey ] ]
			: reset( $this ->limits ));
	}
	
	public function getFormData( )
	{
		if( @$this ->options['mode']['limited'] )
			$this ->setList( $this ->limitkey.'s', $this ->limits );

		if( !@$this ->options['mode']['emptyform'] )
			$this ->setData( $this ->getPostData( ) );
		
		if( @$this ->options['mode']['limited'] )
			$this ->data[ $this ->limitkey ] = $this ->getLimit( );
		
		$r = array( );
		$r['fields'] = $this ->getFieldSet( );
		$r['options'] = $this ->getOptions( );
		
		return $r;
	}

}


